package com.ruoyi.system.protocol.impl;

import com.ruoyi.common.constant.ClassesConstants;
import com.ruoyi.common.utils.IdWorker;
import com.ruoyi.system.domain.SysDataProperty;
import com.ruoyi.system.protocol.IDynamicCacheService;
import com.ruoyi.system.protocol.IDynamicDataService;
import com.ruoyi.system.protocol.IDynamicTableService;
import com.ruoyi.system.service.ISysClassesService;
import com.ruoyi.system.service.ISysDataPropertyService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DynamicDataServiceImpl implements IDynamicDataService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataServiceImpl.class);
    private IdWorker idWorker = new IdWorker(2);
    @Autowired
    private ISysDataPropertyService sysDataPropertyService;
    @Autowired
    private ISysClassesService sysClassesService;
    @Autowired
    private IDynamicCacheService dynamicCache;
    @Autowired
    private IDynamicTableService dynamicTableService;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public List<Map<String, Object>> getDynamicData(String classesEn, List<String> selectField, Map<String, Object> condition, Map<String, Object> sortCondition, Integer pageNum, Integer pageSize) {
        List<Object> pstParams = new ArrayList<>();
        String sql = getListSql(classesEn, selectField, condition, sortCondition, pageNum, pageSize, pstParams);
        return jdbcTemplate.queryForList(sql, pstParams.toArray(new Object[]{}));
    }

    @Override
    public Long getDynamicDataCount(String classesEn, Map<String, Object> condition) {
        List<Object> pstParams = new ArrayList<>();
        String sql = getCountSql(classesEn, condition, pstParams);
        return jdbcTemplate.queryForObject(sql, Long.class, pstParams.toArray(new Object[]{}));
    }

    @Override
    public Map<String, Object> findDynamicData(String classesEn, List<String> selectField, String id) {
        Map<String, Object> condition = new HashMap<>();
        condition.put("=,id", id);
        List<Map<String, Object>> dynamicData = getDynamicData(classesEn, selectField, condition, null, 1, 1);
        if(dynamicData.size() > 0){
            return dynamicData.get(0);
        }
        return null;
    }

    @Override
    public String addDynamicData(String classesEn, Map<String, Object> dynamicData) {
        if(dynamicData == null || dynamicData.size() <= 0)return null;
        String tableName = ClassesConstants.RESOURCE_TABLE_PREFIX + classesEn;
        List<String> columns = dynamicTableService.getColumns(tableName);
        String id = idWorker.nextId()+"";
        if(StringUtils.isNotBlank(dynamicData.get("id") + "") && !"null".equals(dynamicData.get("id") + "")){
            id = dynamicData.get("id") + "";
        }
        List<String> colList = new ArrayList<>();
        colList.add("id");
        List<String> valList = new ArrayList<>();
        valList.add("?");
        List<String> pstParams = new ArrayList<>();
        pstParams.add(id);

        for(String column: columns){
            if(!colList.contains(column)){
                colList.add(column);
                valList.add("?");
                String val = dynamicData.get(column) + "";
                if(StringUtils.isNotBlank(val) && !"null".equals(val)){
                    pstParams.add(val+"");
                }else{
                    pstParams.add("");
                }
            }
        }
        StringBuilder sql = new StringBuilder("INSERT INTO ");
        sql.append(tableName + " ");
        sql.append("( ");
        sql.append(colList.stream().collect(Collectors.joining(","))+" ");
        sql.append(") ");
        sql.append("VALUE ");
        sql.append("( ");
        sql.append(valList.stream().collect(Collectors.joining(","))+" ");
        sql.append(") ");
        int insertCount = jdbcTemplate.update(sql + "", pstParams.toArray(new Object[]{}));
        if(insertCount > 0){
            return id;
        }
        return null;
    }

    @Override
    public boolean addBatchDynamicData(String classesEn, List<Map<String, Object>> dynamicDatas) {
        if(dynamicDatas == null || dynamicDatas.size() <= 0)return false;
        String tableName = ClassesConstants.RESOURCE_TABLE_PREFIX + classesEn;
        List<String> columns = dynamicTableService.getColumns(tableName);
        List<String> colList = new ArrayList<>();
        colList.add("id");
        List<String> valList = new ArrayList<>();
        valList.add("?");
        List<Object[]> batchInsertList = new ArrayList<>();
        List<String> pstParams = null;
        for (Map<String, Object> dynamicData : dynamicDatas) {
            String id = idWorker.nextId()+"";
            if(StringUtils.isNotBlank(dynamicData.get("id") + "") && !"null".equals(dynamicData.get("id") + "")){
                id = dynamicData.get("id") + "";
            }
            pstParams = new ArrayList<>();
            pstParams.add(id);
            for (String column : columns) {
                if(!colList.contains(column)){
                    String val = dynamicData.get(column) + "";
                    if(StringUtils.isNotBlank(val) && !"null".equals(val)){
                        pstParams.add(val+"");
                    }else{
                        pstParams.add("");
                    }
                }
            }
            batchInsertList.add(pstParams.toArray(new Object[]{}));
        }
        for (String column : columns) {
            if (!colList.contains(column)) {
                colList.add(column);
                valList.add("?");
            }
        }
        StringBuilder sql = new StringBuilder("INSERT INTO ");
        sql.append(tableName + " ");
        sql.append("( ");
        sql.append(colList.stream().collect(Collectors.joining(","))+" ");
        sql.append(") ");
        sql.append("VALUE ");
        sql.append("( ");
        sql.append(valList.stream().collect(Collectors.joining(","))+" ");
        sql.append(") ");
        jdbcTemplate.batchUpdate(sql + "", batchInsertList);
        return true;
    }

    @Override
    public boolean editDynamicData(String classesEn, Map<String, Object> dynamicData, Map<String, Object> condition) {
        if(dynamicData == null || dynamicData.size() <= 0)return false;
        String tableName = ClassesConstants.RESOURCE_TABLE_PREFIX + classesEn;
        List<String> columns = dynamicTableService.getColumns(tableName);
        columns.remove("id");
        List<Object> pstParams = new ArrayList<>();
        List<String> setSql = new ArrayList<>();
        StringBuilder sql = new StringBuilder("UPDATE ");
        sql.append(tableName + " ");
        sql.append("SET ");
        for (String column : columns) {
            if(dynamicData.containsKey(column)){
                setSql.add(column+"=?");
                String val = dynamicData.get(column) + "";
                if(StringUtils.isNotBlank(val) && !"null".equals(val)){
                    pstParams.add(val+"");
                }else{
                    pstParams.add("");
                }
            }
        }
        sql.append(setSql.stream().collect(Collectors.joining(",")));
        getConditionalStatement(condition, sql, pstParams);
        int editCount = jdbcTemplate.update(sql + "", pstParams.toArray(new Object[]{}));
        return editCount>0?true:false;
    }

    @Override
    public boolean delDynamicData(String classesEn, Map<String, Object> condition) {
        List<Object> pstParams = new ArrayList<>();
        StringBuilder sql = new StringBuilder("DELETE FROM ");
        sql.append(ClassesConstants.RESOURCE_TABLE_PREFIX + classesEn + " ");
        getConditionalStatement(condition, sql, pstParams);
        int delCount = jdbcTemplate.update(sql+"", pstParams.toArray(new Object[]{}));
        return delCount>0?true:false;
    }


    /**
     * 获取数据查询sql
     * @param classesEn  表名
     * @param condition 查询条件
     * @param sortCondition 排序条件
     * @param pageNum 页面
     * @param pageSize 每页条数
     * @return
     */
    public  String getListSql(String classesEn, List<String> selectField, Map<String, Object> condition, Map<String, Object> sortCondition, Integer pageNum, Integer pageSize, List<Object> pstParams){
        List<SysDataProperty> sysDataProperties = dynamicCache.getDataPropertyListCache(classesEn);
        List<String> tableField = sysDataProperties.stream().map(x -> x.getNameEn()).collect(Collectors.toList());
        String tableName = ClassesConstants.RESOURCE_TABLE_PREFIX + classesEn;
        List<String> columns = dynamicTableService.getColumns(tableName);
        List<String> baseField = new ArrayList<>(columns);
        baseField.removeAll(tableField);
        StringBuilder sql = new StringBuilder("SELECT ");
        if(selectField != null && selectField.size() > 0){
            String selectFieldStr = selectField.stream().collect(Collectors.joining(","));
            sql.append(String.join(",", baseField)+","+selectFieldStr + " ");
        }else{
            sql.append("* ");
        }
        sql.append("FROM ");
        sql.append(tableName + " ");
        getConditionalStatement(condition, sql, pstParams);
        sql.append("ORDER BY ");
        if(sortCondition != null && sortCondition.size() > 0){
            sortCondition.forEach((key,val)->{
                //判断是否为表字段。防注入
                if(columns.contains(key)){
                    sql.append(key+" " +val+", ");
                }
            });
        }
        sql.append("create_time desc ");
        int from = (pageNum - 1) * pageSize;
        int to = pageSize;
        sql.append("LIMIT ?,?");
        pstParams.add(from);
        pstParams.add(to);
        return sql.toString();
    }

    public static String getCountSql(String classesEn, Map<String, Object> condition, List<Object> pstParams){
        StringBuilder sql = new StringBuilder("SELECT count(id) FROM ");
        sql.append(ClassesConstants.RESOURCE_TABLE_PREFIX + classesEn + " ");
        getConditionalStatement(condition, sql, pstParams);
        return sql.toString();
    }

    /**
     * 拼接公共条件语句
     * @param condition 查询条件
     * @param sql 查询语句
     */
    private static void getConditionalStatement(Map<String, Object> condition, StringBuilder sql, List<Object> pstParams) {
        if (condition != null && condition.size() > 0) {
            sql.append("WHERE ( 1=1 ");
            condition.forEach((key, val) -> {
                if(!"$dataScope".equals(key)){
                    String[] operation = key.split(",");
                    if (operation.length > 1) {
                        String operationSymbol = operation[0];
                        String operationVal = operation[1];
                        if (ClassesConstants.BASIC_SYMBOLS.contains(operationSymbol)) {
                            sql.append("AND " + operationVal + " " + operationSymbol + " ? ");
                            pstParams.add(val+"");
                        } else if ("in".equals(operationSymbol)) {
                            if(StringUtils.isNotBlank(val+"") && !"null".equals(val+"")){
                                List<Object> vs = new ArrayList<>();
                                if(val instanceof ArrayList){
                                    vs = (List<Object>)val;
                                }else if(val instanceof Object []){
                                    vs = Arrays.asList((Object[])val);
                                }else{
                                    vs = Arrays.asList((val + "").split(","));
                                }
                                sql.append("AND " + operationVal + " " + operationSymbol + " ( ");
                                sql.append(vs.stream().map(x->"?").collect(Collectors.joining(",")));
                                sql.append(" ) ");
                                pstParams.addAll(vs);
                            }
                        } else if ("like".equals(operationSymbol)) {
                            sql.append("AND " + operationVal + " " + operationSymbol + " concat('%',?,'%') ");
                            pstParams.add(val+"");
                        }
                    } else {
                        sql.append("AND " + operation[0] + "= ? ");
                        pstParams.add(val+"");
                    }
                }
            });
            sql.append(" ) ");
            condition.forEach((key, val) -> {
                if("$dataScope".equals(key)){
                    sql.append(val);
                }
            });
        }
    }



}
